/*
 * Copyright (C) 2008-2012  Paulo Cesar Pereira de Andrade.
 *
 * This is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * Authors:
 *	Paulo Cesar Pereira de Andrade
 */

#ifndef _eobject_h
#define _eobject_h

#include "etypes.h"

/*
 * Defines
 */
#define etype(object)	(((eint32_t *)(object))[-1])
#define gc_enter()	eint32_t	_tgci = gc_offset
#define gc_push(object)							\
    do {								\
	assert(gc_offset < esize(gc_vector));				\
	gc_vector[gc_offset++] = object;				\
    } while (0)
#define gc_ref(pointer)							\
    do {								\
	assert(gc_offset < esize(gc_vector));				\
	pointer = &gc_vector[gc_offset];				\
	gc_vector[gc_offset] = null;					\
	++gc_offset;							\
    } while (0)
#define gc_inc()							\
    do {								\
	assert(gc_offset < esize(gc_vector));				\
	++gc_offset;							\
    } while (0)
#define gc_dec()							\
    do {								\
	assert(gc_offset > 0);						\
	--gc_offset;							\
    } while (0)
#define gc_pop(object)							\
    do {								\
	assert(gc_offset > 0);						\
	object = gc_vector[--gc_offset];				\
    } while (0)
#define gc_top(object)							\
    do {								\
	assert(gc_offset > 0);						\
	object = gc_vector[gc_offset - 1];				\
    } while (0)
#define gc_leave()	gc_offset = _tgci
#define enew_vector(pointer, type, length)				\
    enew_vector_base(pointer,						\
		     type, t_vector | type,				\
		     length, sizeof(evector_t))

/*
 * Types
 */
enum etype {
    t_void,

    t_word,
    t_float,
#  define t_float64		t_float

    t_int8,
    t_uint8,
    t_int16,
    t_uint16,
#if __WORDSIZE == 64
    t_int32,
#else
#  define t_half		t_int16
#  define t_int32		t_word
#endif
    t_uint32,
#if __WORDSIZE == 32
    t_int64,
#else
#  define t_half		t_int32
#  define t_int64		t_word
#endif
    t_uint64,
    t_float32,
#define t_single		t_float32

    t_rat,
    t_mpz,
    t_mpq,
    t_mpr,
    t_cdd,
    t_cqq,
    t_mpc,

    t_thread		= 0x20000000,
    t_rtti,

    t_stream,
    t_input,

    t_hash,
    t_entry,

    t_symbol,

    t_ast,
    t_tag,
    t_value,

    t_basic,
    t_class,
    t_namespace,
    t_function,

    t_null,		/* static null */
    t_bool,		/* zero or one */
    t_integer,		/* bool, word or mpz */
    t_exact,		/* integer or mpq */
    t_inexact,		/* float or mpr */
    t_real,		/* exact or inexact */
    t_number,		/* real or complex */

    t_vector		= 0x40000000,
    t_vector_int8	= t_vector | t_int8,
#define t_string	t_vector_uint8
    t_vector_uint8	= t_vector | t_uint8,
    t_vector_int16	= t_vector | t_int16,
    t_vector_uint16	= t_vector | t_uint16,
    t_vector_int32	= t_vector | t_int32,
    t_vector_uint32	= t_vector | t_uint32,
    t_vector_int64	= t_vector | t_int64,
    t_vector_uint64	= t_vector | t_uint64,
    t_vector_float32	= t_vector | t_float32,
    t_vector_float64	= t_vector | t_float64,
    t_undef		= 0x80000000
};

struct evector {
    union {
	eword_t		*w;
	eint8_t		*i8;
	euint8_t	*u8;
	eint16_t	*i16;
	euint16_t	*u16;
	eint32_t	*i32;
	euint32_t	*u32;
	eint64_t	*i64;
	euint64_t	*u64;
	efloat32_t	*f32;
	efloat64_t	*f64;
	eobject_t	*ptr;
	eobject_t	 obj;
    } v;
    eword_t		 length;
    eword_t		 offset;
};

struct ertti {
    eword_t		 gcsize;	/* number of gc offsets */
    eword_t		*gcinfo;	/* gc pointer offsets */
};

/*
 * Prototypes
 */
extern void
init_object(void);

extern void
finish_object(void);

extern void
eadd_root(eobject_t *object);

extern void
erem_root(eobject_t *object);

extern void
enew_object(eobject_t *pointer, etype_t type, eword_t length);

extern void
enew_word(eobject_t *pointer, eword_t w);

extern void
enew_float(eobject_t *pointer, efloat_t d);

extern void
enew_float_w(eobject_t *pointer, eword_t w);

extern void
enew_rat(eobject_t *pointer, erat_t R);

extern void
enew_rat_w(eobject_t *pointer, eword_t w);

extern void
enew_mpz(eobject_t *pointer, empz_t Z);

extern void
enew_mpz_w(eobject_t *pointer, eword_t w);

extern void
enew_mpq(eobject_t *pointer, empq_t Q);

extern void
enew_mpq_w(eobject_t *pointer, eword_t w);

extern void
enew_mpr(eobject_t *pointer, empr_t R);

extern void
enew_mpr_w(eobject_t *pointer, eword_t w);

extern void
enew_cdd(eobject_t *pointer, ecdd_t dd);

extern void
enew_cdd_w(eobject_t *pointer, eword_t w);

extern void
enew_cqq(eobject_t *pointer, ecqq_t QQ);

extern void
enew_cqq_w(eobject_t *pointer, eword_t w);

extern void
enew_mpc(eobject_t *pointer, empc_t CC);

extern void
enew_mpc_w(eobject_t *pointer, eword_t w);

extern void
enew_vector_base(eobject_t *pointer,
		 etype_t type, etype_t base_type,
		 eword_t length, eword_t base_length);

extern void
erenew_vector(evector_t *vector, eword_t length);

/*
 * Externs
 */
extern ethread_t	*thread_main;
extern evector_t	*rtti_vector;
extern evector_t	*rtdi_vector;

/* simple way to protect several parser temporaries */
extern eint32_t		 gc_offset;
extern eobject_t	 gc_vector[16];

#if GC_DEBUG
/* if set, call gc every at memory allocation */
extern eint32_t		 gc_debug;
#endif

#if CACHE_DEBUG
/*   Check if a referenced object is moved to recycle cache and
 * also for "leaks" missing objects that could be cached, instead
 * of getting unreferenced and collected by gc. */
extern void
gc_cache_debug(eobject_t object);

extern eint32_t		 cache_debug;
#endif

#endif /* _eobject_h */
